// 定义了一种一对多的关系, 让多个观察者同时监听某一个主题对象, 这个主题对象状态发生改变时就会通知所有的观察者, 使他们能自动更新自己, 
// 当一个对象的 改变需要同时改变其他对象, 并且它不知道具体有多少个对象需要改变的时候,就应该考虑使用观察者模式

// 观察者模式和发布订阅模式的区别
// 首先，观察者是经典软件设计模式中的一种，但发布订阅只是软件架构中的一种消息范式。所以不要再被“观察者模式和发布订阅模式xxx”这样的问题误导。

// 其次，就是实现二者所需的角色数量有着明显的区别。观察者模式本身只需要2个角色便可成型，即观察者和被观察者，其中被观察者是重点。而发布订阅需要至少3个角色来组成，包括发布者、订阅者和发布订阅中心，其中发布订阅中心是重点。


// 被观察者,(主题)
// 保存状态,  观察者, 
// 方法 更新, 通知, 绑定观察者
class Subject{
    state:number ;
    observers:any[]
    constructor( ){
        this.state = 0;
        this.observers = []
    }

    // 通知所有的观察着
    notifyObservers(){
        this.observers.forEach( (observer)=>{
            observer.update(this.state);
        })
    }

    // 绑定观察着
    attatch(observer){
        this.observers.push(observer)
    }

    update(state){
        this.state = state;
        this.notifyObservers();
    }


}

// 核心要点: 观察者通过 主题定义的attatch方法把自己注册给主题
// 主题更新时调用观察者的update()方法


// 观察者 自己的属性(name),  状态变化时的处理函数
class Observer {
    name:string
    constructor(name, subject){
        this.name = name;
        subject.attatch(this);
    }

    // 主题状态更新后触发update
    update(state){
        console.log(`${this.name} 状态更新了, 状态是 ${state}`)
    }
}

const s = new Subject();
const o1 = new Observer('o1',s);
const o2 = new Observer('o2',s);
s.update(2)




// 优点
// 支持简单的广播通信，自动通知所有已经订阅过的对象
// 目标对象与观察者之间的抽象耦合关系能单独扩展以及重用
// 增加了灵活性
// 观察者模式所做的工作就是在解耦，让耦合的双方都依赖于抽象，而不是依赖于具体。从而使得各自的变化都不会影响到另一边的变化。

// 缺点
// 过度使用会导致对象与对象之间的联系弱化，会导致程序难以跟踪维护和理解